#include "StdAfx.h"
#include "HttpPool.h"
#include "CriticalSection.h"
#include <afxinet.h>

#include "WorkItem.h"
#include "Data/CharsetConvert.h"

#define  THREADTIMEOUT 100
bool SafeDeleteThread(CWinThread* pWinThread)
{
	if (NULL == pWinThread)
	{
		return false;
	}

	if( WAIT_TIMEOUT == WaitForSingleObject(pWinThread->m_hThread, THREADTIMEOUT))
	{
		TerminateThread(pWinThread->m_hThread,0);
	}
	delete pWinThread;

	return true;
}


CHttpPool::CHttpPool(void)
{
	m_pTaskSection = new CriticalSection;
	m_pMapSection = new CriticalSection;
	m_pStatusSection = new CriticalSection;

	m_nPoolSize = 0;
	m_pWinThread = NULL;

	m_nHttpStatus=  HttpStatus_None;
}

CHttpPool::~CHttpPool(void)
{
	StopAll();


	if (m_pTaskSection)
	{
		delete m_pTaskSection;
		m_pTaskSection = NULL;
	}

	if (m_pMapSection)
	{
		delete m_pMapSection;
		m_pMapSection = NULL;
	}

	if (m_pStatusSection)
	{
		delete m_pStatusSection;
		m_pStatusSection = NULL;
	}

}

void CHttpPool::Init(int nCount)
{
	m_nPoolSize = nCount;
	SetHttpStatus( HttpStatus_Run);
	if (m_pWinThread)
	{
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_INIT,(WPARAM)nCount,NULL);
	}
	
	
}

void CHttpPool::Init_in(int nCount)
{
	m_nPoolSize = nCount;
	for (int i = 0 ; i < nCount ; i++)
	{
		CWorkItem * pItem = new CWorkItem();
		pItem->Init(this);
		m_mapRecvs[pItem]=NULL;
	}
	
}

bool CHttpPool::AddTask(IRecv *pRecv,const char * pBuf,int nLen,const CString& strHost,int nPort)
{
	CritScope scope(m_pTaskSection);

	HttpTaskData *pData = new HttpTaskData;
	pData->pRecv = pRecv;
	pData->pBuf = new char[nLen];
	memcpy(pData->pBuf,pBuf,nLen);

	pData->nLen = nLen;
	pData->strHost = strHost;
	pData->nPort = nPort;
	pData->nHasSended = 0;

	m_qTaskDatas.push(pData);
	if (m_pWinThread)
	{
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_PROCESS,NULL,NULL);
	}
	
	return true;
}

const CString strRangeTemplate = _T("GET %s HTTP/1.1\r\nHost:%s\r\nUser-Agent:Mozilla/5.0\r\nRANGE: bytes=%d-%s\r\nConnection:close\r\n\r\n");

bool CHttpPool::AddRangeUrl(IRecv* pRecv,const CString& strUrl,DWORD dwBegin)
{
	return AddRangeUrl(pRecv,strUrl,dwBegin,0);
}

bool CHttpPool::AddRangeUrl(IRecv* pRecv,const CString& strUrl,DWORD dwBegin,DWORD dwEnd)
{
	DWORD dwServiceType;
	CString strServer;
	CString strObject;
	INTERNET_PORT nPort;
	if(AfxParseURL(strUrl,dwServiceType,strServer,strObject,nPort))
	{
		CString strEnd;
		if (0 != dwEnd)
		{
			strEnd.Format(_T("%d"),dwEnd);
		}
		CString strTemp;
		strTemp.Format(strRangeTemplate,strObject,strServer,dwBegin,strEnd);

		string strBuf = ct2cu(strTemp);
		return AddTask(pRecv,strBuf.c_str(),strBuf.size(),strServer,nPort);

	}

	return false;
}

const CString strTemplate = _T("GET %s HTTP/1.1\r\nHost:%s\r\nUser-Agent:Mozilla/5.0\r\nConnection:close\r\n\r\n");

bool CHttpPool::AddUrl(IRecv* pRecv,const CString& strUrl)
{
	DWORD dwServiceType;
	CString strServer;
	CString strObject;
	INTERNET_PORT nPort;
	if(AfxParseURL(strUrl,dwServiceType,strServer,strObject,nPort))
	{
		CString strTemp;
		strTemp.Format(strTemplate,strObject,strServer);

		string strBuf = ct2cu(strTemp);
		return AddTask(pRecv,strBuf.c_str(),strBuf.size(),strServer,nPort);

	}
	
	return false;
}

const CString strPostTemplate = _T("POST %s HTTP/1.1\r\nHost:%s\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:%d\r\nUser-Agent:Mozilla/5.0\r\nConnection:close\r\n\r\n");


bool CHttpPool::AddPostUrl(IRecv* pRecv,const CString& strUrl,const char* pPostBuf,int nLen)
{
	DWORD dwServiceType;
	CString strServer;
	CString strObject;
	INTERNET_PORT nPort;
	if(AfxParseURL(strUrl,dwServiceType,strServer,strObject,nPort))
	{
		CString strTemp;
		strTemp.Format(strPostTemplate,strObject,strServer,nLen);

		string strBuf = ct2cu(strTemp);
		char * pBuf = new char[strBuf.size()+nLen];
		memcpy(pBuf,strBuf.c_str(),strBuf.size());
		memcpy(pBuf+strBuf.size(),pPostBuf,nLen);
		bool bRet = AddTask(pRecv,pBuf,strBuf.size()+nLen,strServer,nPort);

		delete pBuf;

		return bRet;
	}

	return false;
}


const CString strPostTemplate2 = _T("POST %s HTTP/1.1\r\nHost:%s\r\nContent-Type:text/xml\r\nContent-Length:%d\r\nUser-Agent:Mozilla/5.0\r\nConnection:close\r\n\r\n");


bool CHttpPool::AddPostUrl2(IRecv* pRecv,const CString& strUrl,const char* pPostBuf,int nLen)
{
	DWORD dwServiceType;
	CString strServer;
	CString strObject;
	INTERNET_PORT nPort;
	if(AfxParseURL(strUrl,dwServiceType,strServer,strObject,nPort))
	{
		CString strTemp;
		strTemp.Format(strPostTemplate2,strObject,strServer,nLen);

		string strBuf = ct2cu(strTemp);
		char * pBuf = new char[strBuf.size()+nLen];
		memcpy(pBuf,strBuf.c_str(),strBuf.size());
		memcpy(pBuf+strBuf.size(),pPostBuf,nLen);
		bool bRet = AddTask(pRecv,pBuf,strBuf.size()+nLen,strServer,nPort);

		delete pBuf;

		return bRet;
	}

	return false;
}



void CHttpPool::DoSetContentCoding(CWorkItem* pItem,const string& szCoding)
{
	HttpTaskData * pData = GetItemData(pItem);//it->second;
	if (pData && pData->pRecv)
	{
		pData->pRecv->OnContentEncoding(szCoding);
	}
}

void CHttpPool::DoSetContentLength(CWorkItem* pItem,int nLen)
{
	HttpTaskData * pData = GetItemData(pItem);//it->second;
	if (pData && pData->pRecv)
	{
		pData->pRecv->OnContentLength(nLen);
	}
}

void CHttpPool::DoCallBack(CWorkItem* pItem,char* pBuf,int nLen)
{

	HttpTaskData * pData = GetItemData(pItem);//it->second;
	if (pData && pData->pRecv)
	{
		pData->pRecv->OnRecv(pBuf,nLen);
	}

}

bool CHttpPool::CloseWorkItem(IRecv* pRecv)
{
	CritScope scope(m_pMapSection);
	map<CWorkItem*,HttpTaskData*>::iterator it= m_mapRecvs.begin();
	for (;it != m_mapRecvs.end();it++)
	{
		HttpTaskData* pTaskData = it->second;
		if (pTaskData && pTaskData->pRecv == pRecv)
		{
			CWorkItem * pItem = it->first;
			if (pItem)
			{
				pItem->ShutDown();
				OnClose(pItem);

			}

		}

	}

	return NULL;
}

bool CHttpPool::StopWorkItem(IRecv* pRecv)
{
	
	if (CloseWorkItem(pRecv))
	{	
		
		return true;
	}
	

	return false;
}

HttpTaskData* CHttpPool::GetItemData(CWorkItem* pItem)
{
	CritScope scope(m_pMapSection);
	map<CWorkItem*,HttpTaskData*>::iterator it= m_mapRecvs.find(pItem);
	if (it != m_mapRecvs.end())
	{
		HttpTaskData * pRecv = it->second;
		return pRecv;
	}

	return NULL;
}

void CHttpPool::DelItemData(CWorkItem*pItem)
{
	CritScope scope(m_pMapSection);
	map<CWorkItem*,HttpTaskData*>::iterator it= m_mapRecvs.find(pItem);
	if (it != m_mapRecvs.end())
	{
		CWorkItem * pItem = it->first;
		if (pItem)
		{
			delete pItem;
		}

		HttpTaskData* pData = it->second;
		if (pData)
		{
			if (pData->pRecv)
			{
				pData->pRecv->OnError(ErrorType_Finish);
			}

			if (pData->pBuf)
			{
				delete pData->pBuf;
				pData->pBuf = NULL;
			}

			delete pData;
		}

		m_mapRecvs.erase(it);
		
	}

	
}

void CHttpPool::ClearItem()
{
	CritScope scope(m_pMapSection);
	for (map<CWorkItem*,HttpTaskData*>::iterator it = m_mapRecvs.begin();it!= m_mapRecvs.end();it++)
	{
		CWorkItem * pItem = it->first;
		if (pItem)
		{
			delete pItem;
		}

		HttpTaskData* pData = it->second;
		if (pData)
		{
			if (pData->pBuf)
			{
				delete pData->pBuf;
				pData->pBuf = NULL;
			}

			delete pData;
		}


	}

	m_mapRecvs.clear();
}


void CHttpPool::OnClose(CWorkItem* pItem)
{
	if (m_pWinThread)
	{
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_SOCKCLOSE,(WPARAM)pItem,NULL);
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_PROCESS,NULL,NULL);
	}
	
}

void CHttpPool::OnSendData(CWorkItem* pItem)
{
	if (m_pWinThread)
	{
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_SENDDATA,(WPARAM)pItem,NULL);
	}
	
}

int CHttpPool::GetItemSize()
{
	CritScope scope(m_pMapSection);
	return m_mapRecvs.size();
}

CWorkItem* CHttpPool::GetCurFreeItem()
{
	CritScope scope(m_pMapSection);
	if (m_mapRecvs.size() < (UINT)m_nPoolSize)
	{
		CWorkItem * pItem = new CWorkItem;
		pItem->Init(this);
		m_mapRecvs[pItem] = NULL;

		return pItem;
	}
	else if(m_mapRecvs.size() >(UINT) m_nPoolSize)
	{
		ASSERT(FALSE);
	}

	for (map<CWorkItem*,HttpTaskData*>::iterator it = m_mapRecvs.begin();it!= m_mapRecvs.end();it++)
	{
		if (NULL == it->second)
		{
			return it->first;
		}
	}

	return NULL;
}

void CHttpPool::SetItem(CWorkItem* pItem,HttpTaskData* pRecv)
{
	CritScope scope(m_pMapSection);
	map<CWorkItem*,HttpTaskData*>::iterator it= m_mapRecvs.find(pItem);
	if (it != m_mapRecvs.end())
	{
		it->second = pRecv;
	}

}


int		CHttpPool::GetTaskSize()
{
	CritScope scope(m_pTaskSection);
	return m_qTaskDatas.size();
}


void CHttpPool::PushBackTask(HttpTaskData *pData)
{
	CritScope scope(m_pTaskSection);
	m_qTaskDatas.push(pData);
}

HttpTaskData * CHttpPool::GetFrontTask()
{
	CritScope scope(m_pTaskSection);
	if (m_qTaskDatas.size())
	{
		return m_qTaskDatas.front();
	}

	return NULL;
	
}

void	CHttpPool::PopTask()
{
	CritScope scope(m_pTaskSection);
	if (m_qTaskDatas.size())
	{
		m_qTaskDatas.pop();
	}

}

void	CHttpPool::ClearTask()
{
	CritScope scope(m_pTaskSection);
	while(m_qTaskDatas.size())
	{
		HttpTaskData* pData= m_qTaskDatas.front();
		if (pData)
		{
			if (pData->pBuf)
			{
				delete pData->pBuf;
				pData->pBuf = NULL;
			}

		}

		delete pData;
		m_qTaskDatas.pop();
	}

}
void CHttpPool::SendData(CWorkItem* pItem)
{
	if (pItem)
	{	
		HttpTaskData * pData = GetItemData(pItem);
		if (pData)
		{
			int nRet = pItem->SendData(pData->pBuf+pData->nHasSended,pData->nLen - pData->nHasSended);
			if (nRet>0)
			{
				pData->nHasSended += nRet;
				if (pData->nHasSended < pData->nLen)
				{
					OnSendData(pItem);
				}
				
			}
			else
			{
				if (pData->pRecv)
				{
					pData->pRecv->OnError(ErrorType_Send);
				}

				OnClose(pItem);
			}
			

	
		}

	}
}

void CHttpPool::Process()
{
	if (GetHttpStatus() == HttpStatus_Run)
	{
		CWorkItem * pItem = GetCurFreeItem();

		while(pItem)
		{	
			HttpTaskData * pData = GetFrontTask();
			if (pData)
			{
				if (pItem->DoConnect(pData->strHost,pData->nPort))
				{
					SetItem(pItem,pData);		
				}
				else
				{
					if (pData->pRecv)
					{
						pData->pRecv->OnError(ErrorType_Connect);
					}

					if (pData->pBuf)
					{
						delete pData->pBuf;
						pData->pBuf = NULL;
					}

					delete pData;

					OnClose(pItem);
				}
				PopTask();
			}
			else
			{
				break;
			}

			pItem = GetCurFreeItem();


		}
	}


	
	
}

bool CHttpPool::Stop()
{
	SetHttpStatus( HttpStatus_Stop);

	if(m_pWinThread)
	{
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_STOPALL,NULL,NULL);
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_QUIT,NULL,NULL);
		SafeDeleteThread(m_pWinThread);
		m_pWinThread = NULL;
		return true;
	}
	

	return false;
}


bool CHttpPool::Pause()
{
	SetHttpStatus( HttpStatus_Pause);

	return true;
}

bool CHttpPool::Resume()
{
	SetHttpStatus( HttpStatus_Run);

	if (m_pWinThread)
	{
		::PostThreadMessage(m_pWinThread->m_nThreadID,WM_PROCESS,NULL,NULL);
	}

	return true;
}

void CHttpPool::StopAll()
{
	ClearItem();
	ClearTask();
}

bool CHttpPool::Start()
{
	m_pWinThread = AfxBeginThread(CHttpPool::ThreadFunction,this);

	if (m_pWinThread)
	{	
		m_pWinThread->m_bAutoDelete = FALSE;
		return true;
	}
	return false;
}

UINT CHttpPool::ThreadFunction(void * pData)
{
	CHttpPool* pHP = (CHttpPool*)pData;
	MSG		msg;	
	
	ZeroMemory(&msg,sizeof(msg));
	while(::GetMessage(&msg,NULL,NULL,NULL))
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
		switch(msg.message)
		{
		case WM_PROCESS:
			{
				pHP->Process();
				break;
			}
		case WM_INIT:
			{
				pHP->Init_in((int)msg.wParam);
				break;
			}
		case WM_SENDDATA:
			{
				pHP->SendData((CWorkItem*)msg.wParam);
				break;
			}
		case WM_SOCKCLOSE:
			{
				pHP->DelItemData((CWorkItem*)msg.wParam);
				break;
			}
		case WM_STOPALL:
			{
				pHP->StopAll();
				break;
			}
		default:
			{
				break;
			}
		}
	}
	return 0;
}

HttpStatus CHttpPool::GetHttpStatus()
{
	CritScope scope(m_pStatusSection);

	return m_nHttpStatus;
}

void CHttpPool::SetHttpStatus(HttpStatus nStatus)
{
	CritScope scope(m_pStatusSection);

	m_nHttpStatus = nStatus;
}